# item_editor.py

####################################################################
#
# Author  : Grisson
# Version : 0.90
# UI
# 
#####################################################################

from item_attribute import *
from special_handlers import *
from Tkinter import *
from tkMessageBox import showwarning
from tkFileDialog import askopenfilename, asksaveasfilename

ENGLISH_TOKEN_PATTERN = re.compile(r'[-\w+]')

class MenuMaker(Frame):

    DefaultFont = ('Arial', 8)

    # menu = (name, underline_index, items)
    # item = (label, underline_index, command)
    #        | Separator
    MenuBar = []
    def __init__(self, root = None, menu_bar = None, start_row = 0):
        Frame.__init__(self, root, bd = 2)
        self.grid(sticky = N + W)
        self.menu_bar = menu_bar
        self.row = start_row
        self.make_menu()
        
        
    def make_menu(self):
        column = 0
        for name, underline_index, items in self.menu_bar:
            mb = Menubutton(self, text = name, underline = underline_index, relief = FLAT)
            mb['font'] = self.DefaultFont
            mb.grid(row = self.row, column = column, padx = 8, pady =4, sticky = N + W)
            column += 1
            pulldown = Menu(mb)
            pulldown['font'] = self.DefaultFont
            self.add_menu_items(pulldown, items)
            mb['menu'] = pulldown
        
    def add_menu_items(self, menu, items):

        for item in items:
            if item == 'separator':
                menu.add_separator()
            else:
                assert callable(item[2])
                menu.add_command(label = item[0], underline = item[1], command = item[2])


        
class ItemEditor:

    DeafaultFont = ('Courier New', 12)
    def __init__(self, db_path):

          
          self.root = root = Tk()
          self.editor_name = self.__class__.__name__
          root.title(self.editor_name )
          
          self.row = 0
          self.item_db = []
          self.id_list = []
          self.id_position_map = {}
          
          self.db_file = None
          self.attr_widgets_created = False
          
          self.root.bind_all('<Button-1>', lambda e: e.widget.focus())
          root.minsize(740, 400)
         

          self.create_menu()

          self.create_control_panel()
          
    def create_menu(self):
        menu_bar = [('File', 0, \
                     [ ('Open', 0, self.open_db),
                       ('Save', 0, self.save_db),
                       ('Save as', 0, self.on_save_as)
                     ]
                    ),
                    ('Misc', 0, \
                     [
                         ('switch language', -1, ItemAttribute.switch_langauge)
                     ]    
                    )
                   ]
        MenuMaker(self.root,  menu_bar)
        self.row += 1

    def open_db(self):
        self.db_file = askopenfilename()
        if self.db_file:
            self.load_db()
            self.update_title()

    def update_title(self):
        if self.db_file:
            self.root.title('%s - %s' % (self.editor_name, self.db_file))
    
    def load_db(self):
        
        self.item_db, self.english_schema, self.chinese_schema = load_item_db(self.db_file)
        
        self.id_list = self.item_db.keys()
        self.id_list.sort()
        if self.id_list:
            self.cur_item = self.item_db[self.id_list[0]]
        self.set_count(len(self.id_list))
        # create widget and attribute
        if not self.attr_widgets_created:
            self.create_attribute_widgets()
        
        self.fill_item_shortcutbox()
        
    def save_db(self):
        if not self.item_db:
            return
        assert self.db_file
        save_item_db(self.db_file, self.item_db, \
                     self.english_schema, self.chinese_schema)
        
    def on_save_as(self):
        filename = asksaveasfilename()
        if filename:
            self.db_file = filename
            self.save_db()
            self.update_title()
    
    def fill_item_shortcutbox(self):
        # fill listbox
        self.id_position_map = {}
        self.shortcut_box.delete(0, END)
        idx = 0
        for id in self.id_list:
            item = self.item_db[id]
            self.shortcut_box.insert('end', self.generate_shortcut(item))
            self.id_position_map[id] = idx
            idx += 1
            
    def create_control_panel(self):
        control_panel = Frame(self.root, relief = GROOVE, bd = 2)
        control_panel.grid(row = self.row, sticky = N + W, padx = 4, pady = 4)
        # create total label
        self.item_count = StringVar()
        label = Label(control_panel, textvariable = self.item_count)
        label.grid(row = 0, column = 0, sticky = S)
        label['font'] = MenuMaker.DefaultFont
        self.set_count(len(self.item_db))
      
        # create a listbox to quickly switch between the items              
        self.create_quickview_list(control_panel)

        # create search entry
        self.create_search_entry(control_panel)
        # create confirm button
        btn = Button(control_panel, text = 'confirm change', command = self.save_cur_item_change)
        btn.grid(row = self.row + 1, padx = 4, pady = 4, sticky = N + W)

    def create_attribute_widgets(self):
        count = 0
        column = 0
        self.row += 1
        self.attrs = {}
        edit_panel = Frame(self.root, relief = GROOVE, bd = 2)
        edit_panel.grid(row = self.row, sticky = N + W)
        
        for name in self.english_schema:
              count += 1
              if name.startswith(u'{'):
                  name = name[1: -1]
              setattr(self, name, StringVar(self.root))
              getattr(self, name).set(self.cur_item[name])
              attr = ItemAttribute(name = name, host = self, row = self.row, column = column)
              if is_special_attribute(name):
                  for handler_name, handler in get_attribute_handlers(attr):
                      setattr(attr, handler_name, handler)
                      
              col_delta = attr.create_widget(edit_panel)
              column += col_delta
              if count % 2 == 0:
                  self.row += 1
                  column = 0
                  
        self.attr_widgets_created = True
                  
    def create_quickview_list(self, root):
           
        self.shortcut_box = listbox = Listbox(root, selectmode = SINGLE)
        listbox.grid(row = 1, column = 0, sticky = N, padx = 2, pady = 2)
        listbox.bind('<Double-Button-1>', self.on_select_item)
        yScroll = Scrollbar(root, orient = VERTICAL)
        yScroll.grid(row = 1, column = 1, sticky = W + N + S, pady = 10)
        listbox['yscrollcommand'] = yScroll.set
        yScroll['command'] = listbox.yview
        

    def create_search_entry(self, root):
        Label(root, text = 'search:', font = self.DeafaultFont).grid(padx = 10, pady = 4, row = self.row - 1, column = 2, sticky = N + W)
        self.search_key = StringVar(self.root)
        ent = Entry(root, textvariable = self.search_key, width = 8)
        ent.grid(row = self.row, column = 2, sticky = N + W, padx = 10, pady = 4)
        ent.bind('<KeyPress-Return>', self.search_item)
        ent['font'] = self.DeafaultFont
    
    def search_item(self, e):
        if not self.item_db:
            return showwarning('warning', 'no item')
        search_key = self.search_key.get()
        target = None
        if not search_key:
            return showwarning('warning', 'empty key!')
        if search_key.isdigit():
            # search by id
            sid = int(search_key)
            if sid in self.item_db:
                target = self.item_db[sid]
        else:
            search_key.lower()
            if ENGLISH_TOKEN_PATTERN.match(search_key):
                search = lambda key, item: key in item['name']
            else:
                search = lambda key, item: key in item['jname']

            for item in self.item_db.values():
                if search(search_key, item):
                    target = item
                    break

        if target:        
            self.select_item(self.id_position_map[int(target['id'])])
        else:
            showwarning('warning', 'no matching item found!')
            
            
    def set_count(self, count):
        self.item_count.set('total: %d items' % count)
        
    def select_item(self, idx):        
        last_idx = self.shortcut_box.index(ACTIVE)
        self.shortcut_box.yview_scroll(idx - last_idx, UNITS)
        self.shortcut_box.activate(idx)
        self.shortcut_box.selection_set(idx)

        shortcut = self.shortcut_box.get(idx)
        id = self.parse_id(shortcut)
        assert id in self.item_db
        self.update_item(self.item_db[id])

    def on_select_item(self, e):
        if not self.item_db:
            return showwarning('warning', 'no item')
        w = e.widget
        idx = w.index(ACTIVE)
        self.select_item(idx)
        
    def update_item(self, item):
        self.cur_item = item
        for name in self.english_schema:
            if name.startswith('{'):
                name = name[1:-1]
            attr = getattr(self, name)
            attr.set(self.cur_item[name])
            
    def save_cur_item_change(self):

        if not self.item_db:
            return showwarning('warning', 'no item')
        
        for name in self.english_schema:
            if name.startswith(u'{'):
                  name = name[1: -1]
            self.cur_item[name] = getattr(self, name).get()

    def cur_item_changed(self):
        for name in self.english_schema:
            if name.startswith(u'{'):
                  name = name[1: -1]
            if self.cur_item[name] != getattr(self, name).get():
                return True
        return False
            
    
    def parse_id(self, shortcut):

        id = shortcut.split(' ')[0]
        return int(id)
    
    def generate_shortcut(self, item):
        return ' '.join(item[:3])
        
    
import traceback
def traceback_info_list():
    etype, evalue, tbdata = sys.exc_info()
    sys.exc_clear()
    try:
        tblist = traceback.extract_tb(tbdata)
        tblist = traceback.format_list(tblist)
        if tblist:
            tblist.insert(0, "Exception traceback:\n")
        tblist[len(tblist):] = traceback.format_exception_only(etype, evalue)
        return tblist
    finally:
        etype = evalue = tbdata = None

def test():
    try:
        app = ItemEditor(r'D:\Games\ro\server\db\item_db.txt')
        app.root.mainloop()
    except Exception, e:
        print ''.join(traceback_info_list())
        showwarning('exception', repr(e))
    return app

if __name__ == '__main__':
    a = test()
   
    
    

                              
              
                
          

    

    

    

        
        
        
        
